#include <algorithm/analytical_model/algorithm/dnn/backend/openvino/CompiledModelCache.h>
#include <utils/Format.h>
#include <utils/ParaUtils.h>
#include <utils/Print.h>
#include <utils/config.h>

#include <filesystem>

auto solar::dnn::openvino::CompiledModelCache::getCompiledModel(
    std::string_view model_path, DeviceType device_type) -> ov::CompiledModel&
{
    if (!std::filesystem::exists(model_path.data()))
    {
        const auto error_msg = solar::format("[ERROR] In function {}: \n    No such file: {}",
                                             PRETTY_FUNCTION_NAME, model_path);
        solar::println(error_msg);
        throw std::runtime_error(error_msg);
    }
    if (this->compiled_model_cache_.contains(model_path.data()))
    {
        auto& cache_item = this->compiled_model_cache_.at(model_path.data());
        if (cache_item.contains(device_type))
        {
            auto& model_state = this->model_cache_.at(model_path.data());
            if (std::filesystem::last_write_time(model_path.data()) == model_state.last_write_time_)
            {
                return cache_item.at(device_type);
            }
        }
    }

    this->updateCompiledModel(model_path, device_type);
    return this->compiled_model_cache_.at(model_path.data()).at(device_type);
}

void solar::dnn::openvino::CompiledModelCache::updateCompiledModel(std::string_view model_path,
                                                                   DeviceType device_type)
{
    std::shared_ptr<ov::Model> current_model = nullptr;
    bool has_latest_model = false;
    if (this->model_cache_.contains(model_path.data()))
    {
        auto& model_state = this->model_cache_.at(model_path.data());
        if (std::filesystem::last_write_time(model_path.data()) == model_state.last_write_time_)
        {
            has_latest_model = true;
            current_model = model_state.model_;
        }
    }
    if (!has_latest_model)
    {
        ModelState current_model_state = {std::filesystem::last_write_time(model_path.data()),
                                          this->core_engine_.read_model(model_path.data())};
        this->model_cache_[model_path.data()] = current_model_state;
        current_model = current_model_state.model_;
    }

    auto& cache_item = this->compiled_model_cache_[model_path.data()];
    cache_item[device_type] =
        this->core_engine_.compile_model(current_model, solar::getEnumName(device_type));
    this->compiled_model_cache_[model_path.data()] = cache_item;
}

void solar::dnn::openvino::CompiledModelCache::clear()
{
    this->model_cache_.clear();
    this->compiled_model_cache_.clear();
}
